package com.hfb.mashibing.alip8.designpattern._12proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * Q：现有如下代码，问题：我想记录坦克的移动时间，该怎么做？
 * A：修改源码，做时间记录 V2
 * Q：那如果无法修改源码的情况下，如何实现呢？V3
 * A：用继承,继承他的实现类 V4
 * Q：应该用代理吧 V5 (持有代理对象实现类的引用，并实现代理接口)
 * Q：V6 代理有各种类型，如何实现代理的各种组合？继承？组合？
 * Q: V7 代理对象改为接口 (持有代理对象实现类的接口的引用，并实现该接口)
 * Q: V8 如果想让LogProxy 可以重用，不仅可以代理日志，
 * 还可以代理其他可以代理的类型 Object
 * （毕竟日志记录，时间计算是喝多系统都用到的东西）这个时候该怎么做呢？
 * A: 分离代理行为与被代理对象——使用jdk动态代理
 *
 * A: V9 横切 代码月业务逻辑代码 分离——AOP
 * A: V10 通过反射观察代理的生成对象
 *      JDK反射生成代理必须面向接口，
 *          因为生成的代理类继承了Proxy，也实现了被代理的接口，（java单继承）
 *          这是由 Proxy 的内部实现决定的
 */

public class V10Tank implements MovableV10 {
    public static void main(String[] args) {
    V10Tank tank = new V10Tank();

    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); // 1.8
    System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true"); //1.8+

    // reflection:通过二进制字节码分析类的属性和方法
    MovableV10 m = (MovableV10) Proxy.newProxyInstance(tank.getClass().getClassLoader(),
        new Class[]{MovableV10.class}, // tank.getClass().getInterfaces()
        new TimeProxyV10(tank)
    );
    m.move();
}


    @Override
    public void move() {
        System.out.println("V10 tank is moving.......");
        try {
            TimeUnit.SECONDS.sleep(new Random().nextInt(5));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("V10 tank is move end......");
    }

}

class TimeProxyV10 implements InvocationHandler{

    private MovableV10 m;

    public TimeProxyV10(MovableV10 m) {
        this.m = m;
    }
    public void before(){System.out.println("before：method start。。。");}
    public void after(){System.out.println("after：method end。。。");}
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//        Arrays.stream(proxy.getClass().getMethods()).map(Method::getName).forEach(System.out::println);

        before();
        Object o = method.invoke(m, args);
        after();
        return o;
    }
}


interface MovableV10 {
    void move();
}
